
#pragma once

#include <tbb/pipeline.h>
#include <tbb/tick_count.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>

#include "TempFile.h"
#include "FileLoader.h"
#include "HashFunction.h"

using namespace std;

class tbbLoader: public tbb::filter
{
	private:
		FileLoader *loader;
	public:
		tbbLoader(TempFile &input_file)
			:filter(true)
		{
			loader = new FileLoader(input_file.getFileName());
		}
		void *operator()(void*)
		{
			return (void *)loader->loadNext();
		}
		~tbbLoader()
		{
			SAFE_DELETE(loader);
		}
};

class tbbHasher: public tbb::filter
{
	public:
		tbbHasher()
			:filter(false)
		{ /*nothing*/ }

		void* operator()(void* item)
		{
			unsigned __int64 *res = new unsigned __int64;
			*res = hashFunc((Buffer<char> *)item);
			return res;
		}
};

class tbbGatherer: public tbb::filter
{
	private:
		unsigned __int64 hash_value;
	public:
		tbbGatherer()
			:filter(true)
		{ hash_value = 0; }

		inline unsigned __int64 getValue()
		{ return hash_value; }

		void *operator()(void* item)
		{
			hash_value += *((unsigned __int64 *)item);
			SAFE_DELETE(item);
			return NULL;
		}
};

//Performs parallel file hashing (via tbb) and returns hash-value + elapsed time.
std::pair<unsigned __int64, double> tbbHashing(TempFile &file)
{
    //Creating pipeline.
    tbb::pipeline pipeline;
	//Creating filter tbbLoader.
    tbbLoader loader(file);
    pipeline.add_filter(loader);
	//Creating filter tbbHasher.
    tbbHasher hasher; 
    pipeline.add_filter(hasher);
    //Creating filter tbbGatherer.
    tbbGatherer gatherer;
    pipeline.add_filter(gatherer);
    //Launching pipeline.
    tbb::tick_count t0 = tbb::tick_count::now();
    pipeline.run(256);
    tbb::tick_count t1 = tbb::tick_count::now();
	unsigned __int64 res = gatherer.getValue();
    pipeline.clear(); 
	return std::pair<unsigned __int64, double>(res, (t1 - t0).seconds());
}
